package main

import (
	"context"
	"errors"
	"flag"
	"net/http"
	"time"

	"github.com/go-playground/validator"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// Config file
type config struct {
	Debug       bool
	Listen      string
	BehindProxy bool
}

type BaseModel struct {
	ID        uint           `json:"id"          gorm:"primaryKey"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	DeletedAt gorm.DeletedAt `json:"-"           gorm:"index"`
}

type ExtendedContext struct {
	echo.Context
}

func (c *ExtendedContext) DB() *gorm.DB {
	return newDBSession(c)
}

// Validator Setting
type CustomValidator struct {
	validator *validator.Validate
}

func (cv *CustomValidator) Validate(i interface{}) error {
	if err := cv.validator.Struct(i); err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
	}
	return nil
}

var (
	Config   config
	NeedAuth echo.MiddlewareFunc
	DB       *gorm.DB
)

// 把初始化分离出 main， 方便单元测试。执行之前需要初始化 Config
func Load() *echo.Echo {
	var err error
	// 初始化设置
	e := echo.New()
	e.Debug = Config.Debug
	e.HideBanner = true
	if Config.BehindProxy {
		e.IPExtractor = echo.ExtractIPFromXFFHeader()
	} else {
		e.IPExtractor = echo.ExtractIPDirect()
	}
	e.Pre(middleware.AddTrailingSlash())
	e.Validator = &CustomValidator{validator: validator.New()}

	// 设置数据库
	DB, err = gorm.Open(sqlite.Open("db.sqlite3"), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}
	DB.AutoMigrate(&Post{}, &Comment{}, &User{}, &Key{})

	// 扩展 Context
	e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			cc := &ExtendedContext{c}
			return next(cc)
		}
	})

	NeedAuth = middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{
		Validator: func(key string, c echo.Context) (bool, error) {
			db := newDBSession(c)
			var user User
			if result := db.Model(&Key{}).Select("users.*").Omit("users.password").Where("key = ? AND expire > ? AND users.is_active = true", key, time.Now().Local()).Joins("left join users on keys.user_id = users.id").Scan(&user); result.Error != nil || user.Username == "" {
				return false, errors.New("Key 不存在或者已经过期，或者用户被禁用")
			}
			user.Password = ""
			ctx := context.WithValue(c.Request().Context(), "user", &user)
			c.SetRequest(c.Request().WithContext(ctx))
			return true, nil
		},
	})

	// 注册各个模块
	PostRegister(e)
	CommentRegister(e)
	AuthRegister(e)

	return e
}

func main() {
	flag.BoolVar(&Config.Debug, "debug", false, "开启调试模式(false)")
	flag.StringVar(&Config.Listen, "listen", "127.0.0.1:8000", "监听地址(27.0.0.1:8000)")
	flag.BoolVar(&Config.BehindProxy, "behind-proxy", false, "是否跑在代理服务器之后(false)")
	flag.Parse()
	e := Load()
	e.Logger.Fatal(e.Start(Config.Listen))
}
